package mrpanyu.guitool.ext.hotkey;

import com.github.kwhat.jnativehook.GlobalScreen;
import com.github.kwhat.jnativehook.NativeHookException;
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener;

/**
 * 全局热键工具
 * <p>
 * 使用register方法注册热键，unregister方法取消注册
 * <p>
 * trigger - 触发条件 action - 触发动作 afterRelease - 是否按键释放后再触发
 */
public class Hotkey {

	static {
		try {
			GlobalScreen.registerNativeHook();
		} catch (NativeHookException e) {
			throw new RuntimeException(e);
		}
	}

	private HotkeyTrigger trigger;
	private Runnable action;
	private boolean afterRelease;
	private NativeKeyListener keyListener;

	private volatile boolean triggeredFlag = false;
	private volatile boolean ctrlReleased = false;
	private volatile boolean shiftReleased = false;
	private volatile boolean altReleased = false;
	private volatile boolean keyReleased = false;

	public Hotkey(HotkeyTrigger trigger, Runnable action, boolean afterRelease) {
		this.trigger = trigger;
		this.action = action;
		this.afterRelease = afterRelease;
		this.keyListener = new NativeKeyListener() {
			@Override
			public void nativeKeyPressed(NativeKeyEvent event) {
				if (event.getKeyCode() == trigger.getKeyCode()) {
					int modifier = event.getModifiers();
					boolean modifierMatch = true;
					if (trigger.isCtrl()) {
						modifierMatch = (modifier & NativeKeyEvent.CTRL_MASK) > 0;
					} else {
						modifierMatch = (modifier & NativeKeyEvent.CTRL_MASK) == 0;
					}
					if (modifierMatch) {
						if (trigger.isShift()) {
							modifierMatch = (modifier & NativeKeyEvent.SHIFT_MASK) > 0;
						} else {
							modifierMatch = (modifier & NativeKeyEvent.SHIFT_MASK) == 0;
						}
					}
					if (modifierMatch) {
						if (trigger.isAlt()) {
							modifierMatch = (modifier & NativeKeyEvent.ALT_MASK) > 0;
						} else {
							modifierMatch = (modifier & NativeKeyEvent.ALT_MASK) == 0;
						}
					}
					if (modifierMatch) {
						if (afterRelease) {
							triggeredFlag = true;
							ctrlReleased = false;
							shiftReleased = false;
							altReleased = false;
							keyReleased = false;
						} else {
							action.run();
						}
					}
				}
			}

			@Override
			public void nativeKeyReleased(NativeKeyEvent event) {
				if (afterRelease && triggeredFlag) {
					if (event.getKeyCode() == NativeKeyEvent.VC_CONTROL) {
						ctrlReleased = true;
					} else if (event.getKeyCode() == NativeKeyEvent.VC_SHIFT) {
						shiftReleased = true;
					} else if (event.getKeyCode() == NativeKeyEvent.VC_ALT) {
						altReleased = true;
					} else if (event.getKeyCode() == trigger.getKeyCode()) {
						keyReleased = true;
					}
					if (keyReleased) {
						boolean allReleased = true;
						if (trigger.isCtrl() && !ctrlReleased) {
							allReleased = false;
						} else if (trigger.isShift() && !shiftReleased) {
							allReleased = false;
						} else if (trigger.isAlt() && !altReleased) {
							allReleased = false;
						}
						if (allReleased) {
							triggeredFlag = false;
							ctrlReleased = false;
							shiftReleased = false;
							altReleased = false;
							keyReleased = false;
							action.run();
						}
					}
				}
			}

		};
	}

	public HotkeyTrigger getTrigger() {
		return trigger;
	}

	public void setTrigger(HotkeyTrigger trigger) {
		this.trigger = trigger;
	}

	public Runnable getAction() {
		return action;
	}

	public void setAction(Runnable action) {
		this.action = action;
	}

	public boolean isAfterRelease() {
		return afterRelease;
	}

	public void setAfterRelease(boolean afterRelease) {
		this.afterRelease = afterRelease;
	}

	public void register() {
		GlobalScreen.addNativeKeyListener(keyListener);
	}

	public void unregister() {
		GlobalScreen.removeNativeKeyListener(keyListener);
	}

}
